import * as THREE from "three";

export class DynamicTexture {
  canvas:HTMLCanvasElement = document.createElement("canvas")   ;
  context;
  texture;

  DEFAULT_VALUES;

  /**
   * init a dynamic texture with a underlying canvas
   *
   * @param {Number} width  width of the canvas
   * @param {Number} height height of the canvas
   */
  constructor(width: any, height: any) {
    const canvas:HTMLCanvasElement = document.createElement("canvas")  ;
    canvas.width = width;
    canvas.height = height;

    this.canvas = canvas;
    this.context = canvas.getContext("2d");
    this.texture = new THREE.Texture(canvas);

    this.DEFAULT_VALUES = {
      text: "",
      faceColor: "white",
      faceRotation: 0,
      fontFamily: "Arial",
      fontColor: "black",
      fontStyle: "normal",
      fontSizeScale: (resolution: any) => resolution / 1.8,
      edgeThickness: 0.1,
      edgeColor: "black",
      resolution: 200,
      // xPlusFaceProperty: null,
      // xMinusFaceProperty: null,
      // yPlusFaceProperty: null,
      // yMinusFaceProperty: null,
      // zPlusFaceProperty: null,
      // zMinusFaceProperty: null,
    };
  }

  /**
   * draw text
   *
   * @param  {String}             text        - the text to display
   * @param  {Number|undefined}   x           - if provided, it is the x where to draw, if not, the text is centered
   * @param  {Number}             y           - the y where to draw the text
   * @param  {String}             fillStyle   - the fillStyle to clear with, if not provided, fallback on .clearRect
   * @param  {String|undefined}   contextFont - the font to use
   * @return {DynamicTexture}                 - the object itself, for chained texture
   */
  drawText(
    text: string,
    x: number,
    y: number,
    fillStyle: any,
    contextFont = undefined
  ) {
    // set font if needed
    if (contextFont !== undefined) this.context.font = contextFont;
    // if x isn't provided
    if (x === undefined || x === null) {
      const textSize = this.context.measureText(text);
      x = (this.canvas.width - textSize.width) / 2;
    }
    // actually draw the text
    this.context.fillStyle = fillStyle;
    this.context.fillText(text, x, y);
    // make the texture as .needsUpdate
    this.texture.needsUpdate = true;
    // for chained API
    return this;
  }

  drawTextCooked(options: any) {
    const context = this.context;
    const canvas = this.canvas;
    options = options || {};
    let text = options.text;
    const params = {
      margin: options.margin !== undefined ? options.margin : 0.1,
      lineHeight: options.lineHeight !== undefined ? options.lineHeight : 0.1,
      align: options.align !== undefined ? options.align : "left",
      fillStyle: options.fillStyle !== undefined ? options.fillStyle : "black",
      font:
        options.font !== undefined
          ? options.font
          : "bold " + 0.2 * 512 + "px Arial",
    };
    // sanity check
    // eslint-disable-next-line no-console
    console.assert(typeof text === "string");

    context.save();
    context.fillStyle = params.fillStyle;
    context.font = params.font;

    let x: number = 0;
    let y: number = (params.lineHeight + params.margin) * canvas.height;
    while (text.length > 0) {
      // compute the text for specifically this line
      const maxText = computeMaxTextLength(text);
      // update the remaining text
      text = text.substr(maxText.length);

      // compute x based on params.align
      const textSize = context.measureText(maxText);
      if (params.align === "left") {
        const x1 = params.margin * canvas.width;
        console.log(x1);
      } else if (params.align === "right") {
        const x2 = (1 - params.margin) * canvas.width - textSize.width;
        console.log(x2);
      } else if (params.align === "center") {
        const x3 = (canvas.width - textSize.width) / 2;
        console.log(x3);
        // eslint-disable-next-line no-console
      } else console.assert(false);

      // actually draw the text at the proper position
      this.context.fillText(maxText, x, y);

      // goto the next line
      y += params.lineHeight * canvas.height;
    }
    context.restore();

    // make the texture as .needsUpdate
    this.texture.needsUpdate = true;
    // for chained API
    return this;

    function computeMaxTextLength(text: string) {
      let maxText = "";
      const maxWidth = (1 - params.margin * 2) * canvas.width;
      while (maxText.length !== text.length) {
        const textSize = context.measureText(maxText);
        if (textSize.width > maxWidth) break;
        maxText += text.substr(maxText.length, 1);
      }
      return maxText;
    }
  }

  drawImage(/* same params as context2d.drawImage */ ...args: any) {
    // call the drawImage
    this.context.drawImage.apply(this.context, args);
    // make the texture as .needsUpdate
    this.texture.needsUpdate = true;
    // for chained API
    return this;
  }

  /**
   * draw cube text
   */
  updateFaceTexture(faceName: string, initialValues = {}) {
    const model = Object.assign({}, this.DEFAULT_VALUES, initialValues);

    const prop = {
      ...model,
      //@ts-ignore
      ...model[`${faceName}FaceProperty`],
    };

    const canvas = this.canvas;
    const context = this.context;

    // set canvas resolution
    canvas.width = prop.resolution;
    canvas.height = prop.resolution;

    // set background color
    context.fillStyle = prop.faceColor;
    context.fillRect(0, 0, canvas.width, canvas.height);

    // draw edge
    if (prop.edgeThickness > 0) {
      context.strokeStyle = prop.edgeColor;
      context.lineWidth = prop.edgeThickness * canvas.width;
      context.strokeRect(0, 0, canvas.width, canvas.height);
    }

    // set face rotation
    context.save();

    // vertical flip
    // context.translate(0, canvas.height);
    // context.scale(1, -1);

    context.translate(canvas.width / 2, canvas.height / 2);
    context.rotate(-Math.PI * (prop.faceRotation / 180.0));

    // set foreground text
    const textSize = prop.fontSizeScale(prop.resolution);
    context.fillStyle = prop.fontColor;
    context.textAlign = "center";
    context.textBaseline = "middle";
    context.font = `${prop.fontStyle} ${textSize}px "${prop.fontFamily}"`;
    context.fillText(prop.text, 0, 0);

    context.restore();
    this.texture.needsUpdate = true;
    return this;
  }

  /**
   * clear the canvas
   *
   * @param  {String} fillStyle   the fillStyle to clear with, if not provided, fallback on .clearRect
   * @return {DynamicTexture}     the object itself, for chained texture
   */
  clear(fillStyle: any) {
    // depends on fillStyle
    if (fillStyle !== undefined) {
      this.context.fillStyle = fillStyle;
      this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
    } else {
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }
    // make the texture as .needsUpdate
    this.texture.needsUpdate = true;
    // for chained API
    return this;
  }
}
